package com.zh.common.ktx

import android.app.Service
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.FileUtils
import android.os.Vibrator
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Log
import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.SizeUtils
import com.blankj.utilcode.util.Utils
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.zh.common.listener.DownloadFileListener
import com.zh.common.utils.EmptyUtils
import com.zh.common.utils.LiveDataBus
import com.zh.common.utils.toast.ZToastUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.jessyan.autosize.utils.ScreenUtils
import java.io.*
import java.math.RoundingMode
import java.net.FileNameMap
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
import java.text.DecimalFormat
import java.util.regex.Pattern

/**
 * @auth zh
 * @time 2021/11/11 - 17:52
 * @desc 扩展函数
 */
class CommonExp {}

val gson = GsonBuilder().disableHtmlEscaping().create()

/**
 * 获取状态栏高度
 */
val statusBarHeight: Int = ScreenUtils.getStatusBarHeight()
val navigationBarHeight: Int = ScreenUtils.getHeightOfNavigationBar(ActivityUtils.getTopActivity())

/**
 * 四种像素转换
 */
val Number.dp2px: Int get() = SizeUtils.dp2px(this.toFloat())
val Number.px2dp: Int get() = SizeUtils.px2dp(this.toFloat())
val Number.sp2px: Int get() = SizeUtils.sp2px(this.toFloat())
val Number.px2sp: Int get() = SizeUtils.px2sp(this.toFloat())

/**
 * 获取颜色
 */
fun Context.color(color: Int): Int = ContextCompat.getColor(this, color)

fun Int.color(): Int = ContextCompat.getColor(ActivityUtils.getTopActivity(), this)

/**
 * 获取文字
 */
fun Context.string(string: Int): String = this.resources.getString(string)

fun Int.string(): String = ActivityUtils.getTopActivity().resources.getString(this)

/**
 * 去掉字符串首尾空格，中间空格不做处理
 */
fun String.regexSpace(): String {
    return if (EmptyUtils.isNotEmpty(this)) Pattern.compile("^\\s*|\\s*$").matcher(this)
        .replaceAll("") else ""
}

/**
 * 保留一位小数限制
 */
fun Double.oneDecimal(): String {
    val df = DecimalFormat("0.0")
    df.roundingMode = RoundingMode.HALF_UP
    return df.format(this)
}

fun String.oneDecimal(): String = this.toDouble().oneDecimal()

/**
 * 保留两位小数限制
 */
fun Double.twoDecimal(): String {
    val df = DecimalFormat("0.00")
    df.roundingMode = RoundingMode.HALF_UP
    return df.format(this)
}

fun String.twoDecimal(): String = this.toDouble().twoDecimal()

/**
 * 保留整数限制
 */
fun Double.noDecimal(): String {
    val df = DecimalFormat("0")
    df.roundingMode = RoundingMode.HALF_UP
    return df.format(this)
}

fun String.noDecimal(): String = this.toDouble().noDecimal()

/**
 * 必须 两位小数 或 整数
 */
fun Double.twoOrNoneDecimal(): String {
    val df = DecimalFormat("0.00")
    df.roundingMode = RoundingMode.HALF_UP
    var num: String = df.format(this)
    if (!TextUtils.isEmpty(num) && num.endsWith(".00")) {
        num = num.substring(0, num.length - 3)
    } else if (!TextUtils.isEmpty(num) && num.endsWith(".0")) {
        num = num.substring(0, num.length - 2)
    }
    return num
}

fun String.twoOrNoneDecimal(): String = this.toDouble().twoOrNoneDecimal()

/**
 * 必须在主线程调用
 */
inline fun <reified T> livePostValue(key: String, value: T) {
    LiveDataBus.get().with(key).postValue(value)
}

fun livePostValue(key: String) {//默认参数
    LiveDataBus.get().with(key).postValue("myKey")
}

/**
 * 发送一个任务到主线程，可以在子线程中更新数据
 * 多个任务被发送时，只有最后一个会被执行
 */
inline fun <reified T> liveSetValue(key: String, value: T) {
    LiveDataBus.get().with(key).value = value
}

fun liveSetValue(key: String) {//默认参数
    LiveDataBus.get().with(key).value = "myKey"
}

/**
 * LiveData监听数据变化
 */
inline fun <reified T> LifecycleOwner.liveObserve(key: String, crossinline block: (T) -> Unit) {
    LiveDataBus.get().with(key).observe(this) { block(it as T) }
}

fun LifecycleOwner.liveObserve(key: String, block: () -> Unit) {//默认无参数
    LiveDataBus.get().with(key).observe(this) { block() }
}

/**
 * LiveData监听数据变化 - 粘性事件
 * @param isOneAccept 是否只接收一次粘性事件
 */
inline fun <reified T> LifecycleOwner.liveObserveSticky(
    key: String, isOneAccept: Boolean = false, crossinline block: (T) -> Unit
) {
    LiveDataBus.get().with(key).observeSticky(this, isOneAccept) { block(it as T) }
}

fun LifecycleOwner.liveObserveSticky(
    key: String, isOneAccept: Boolean = false, block: () -> Unit
) {//默认无参数
    LiveDataBus.get().with(key).observeSticky(this, isOneAccept) { block() }
}

/**
 * 提示短toast
 */
fun toast(content: Any?, isCenter: Boolean = false) {
    ZToastUtils.showShort(content?.toString(), isCenter)
}

fun Any?.toast() {
    toast(this)
}

fun toastImage(content: Any?, isCenter: Boolean = false, icon: Int? = null) {
    ZToastUtils.showShort(content?.toString(), isCenter, icon)
}

fun Any?.toastImage(icon: Int? = null) {
    toastImage(this, icon = icon)
}

/**
 * 提示长toast
 */
fun toastLong(content: Any?, isCenter: Boolean = false) {
    ZToastUtils.showLong(content?.toString(), isCenter)
}
fun toastLongImage(content: Any?, isCenter: Boolean = false, icon: Int? = null) {
    ZToastUtils.showLong(content?.toString(), isCenter, icon)
}
fun Context.saveFile(view: View) {
    view.isDrawingCacheEnabled = true
    saveFile(view.drawingCache)
}

/**
 * 保存图片
 */
fun Context.saveFile(bitmap: Bitmap) {
    val dciFile = File(getExternalFilesDir(""), "DCIM")
    if (!dciFile.exists()) {
        dciFile.mkdirs()
    }
    val file = File(dciFile, System.currentTimeMillis().toString() + ".jpg")
    val outputStream = FileOutputStream(file)
    val mimeType = getMimeType(file)
    bitmap.compress(Bitmap.CompressFormat.JPEG, 95, outputStream)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val fileName = file.name
        val values = ContentValues()
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
        values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
        val contentResolver: ContentResolver = contentResolver
        val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        if (uri == null) {
            toast("图片保存失败")
            return
        }
        try {
            val out: OutputStream = contentResolver.openOutputStream(uri)!!
            val fis = FileInputStream(file)
            FileUtils.copy(fis, out)
            fis.close()
            out.close()
            toast("图片保存成功")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    } else {
        MediaScannerConnection.scanFile(
            Utils.getApp(), arrayOf(file.path), arrayOf(mimeType)
        ) { path: String, uri: Uri? ->
            toast("图片已成功保存到$path")
        }
    }
}

fun getMimeType(file: File): String {
    val fileNameMap: FileNameMap = URLConnection.getFileNameMap()
    return fileNameMap.getContentTypeFor(file.name)
}

/**
 * 转化为对象
 */
fun <T> fromJsonObj(json: String?, clazz: Class<T>): T? {
    return gson.fromJson(json, clazz)
}

inline fun <reified T> fromJsonList(array: String?): List<T>? {
    return gson.fromJson(array, object : TypeToken<List<T>>() {}.type)
}

inline fun <reified T> fromJsonMap(array: String?): Map<String, T>? {
    return gson.fromJson(array, object : TypeToken<Map<String, T>>() {}.type)
}

/**
 * 对象转化为json格式串
 */
fun <T> toJson(obj: T): String? {
    return gson.toJson(obj)
}

/**
 * 保存文件
 * @param url 下载地址
 * @param path 文件地址
 * @param listener 下载进度监听
 */
fun FragmentActivity.downloadFile(url: String, path: String, listener: DownloadFileListener) {
    lifecycleScope.launch(Dispatchers.IO) {
        withContext(Dispatchers.Main) { listener.start() }
        val file = File(path)
        if (file.exists()) file.delete()
        file.createNewFile()
        var connection: HttpURLConnection? = null
        try {
            connection = URL(url).openConnection() as HttpURLConnection?
            connection?.let { connection ->
                connection.instanceFollowRedirects = true
                connection.requestMethod = "GET"
                connection.connectTimeout = 8000
                connection.readTimeout = 5000
                connection.doInput = true
                connection.doInput = true
                connection.useCaches = false

                if (connection.responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
                    connection.responseCode == HttpURLConnection.HTTP_MOVED_PERM
                ) {//重定向
                    val url2 = URL(connection.getHeaderField("Location"))
                    downloadFile(url2.toString(), path, listener)
                } else if (connection.responseCode == HttpURLConnection.HTTP_OK
                    || connection.responseCode == HttpURLConnection.HTTP_PARTIAL
                ) {
                    var fileSize = connection.contentLength.toLong()
                    if (fileSize < 0) {
                        Log.d("downloadFile", "无法获取文件大小")
                        fileSize = 1024
                    }
                    var fileSizeDownloaded: Long = 0
                    val `in` = connection.inputStream
                    val out = FileOutputStream(file)
                    val buf = ByteArray(4096)
                    try {
                        while (true) {
                            val read = `in`.read(buf)
                            if (read == -1) {
                                break
                            }
                            out.write(buf, 0, read)
                            fileSizeDownloaded += read.toLong()

                            //计算当前下载百分比，并经由回调传出
                            withContext(Dispatchers.Main) {
                                val progress = (100 * fileSizeDownloaded / fileSize).toInt()
                                listener.progress(progress, fileSize)
                            }
                            Log.d("downloadFile", "file download: $fileSizeDownloaded of $fileSize")
                        }
                        Log.d("downloadFile", "下载成功:" + file.name)
                        withContext(Dispatchers.Main) { listener.success(path) }
                    } catch (e: Exception) {
                        e.printStackTrace()
                        Log.d("downloadFile", "下载失败:" + file.name)
                        withContext(Dispatchers.Main) { listener.error(e.message.toString()) }
                    } finally {
                        out.flush()
                        out.close()
                        `in`.close()
                    }
                } else {
                    Log.d("downloadFile", "服务器错误：${connection.responseCode}")
                    withContext(Dispatchers.Main) {
                        listener.error("服务器错误：${connection.responseCode}")
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.d("downloadFile", "下载失败:" + file.name)
            withContext(Dispatchers.Main) { listener.error(e.message.toString()) }
        } finally {
            connection?.disconnect()
            withContext(Dispatchers.Main) { listener.end() }
        }
    }
}

//判断是不是竖屏状态
fun Context.activityIsPortrait(): Boolean =
    ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT == this.resources.configuration.orientation

//更新图库
fun Context.notifyGallery(path: String) {
    MediaScannerConnection.scanFile(this, arrayOf<String>(path), null, null)
}

fun Context.vibrateSearch() {
    vibrate(this, longArrayOf(100, 200), -1)
}

private fun vibrate(context: Context, milliseconds: LongArray?, repeat: Int) {
    val vib = context.getSystemService(Service.VIBRATOR_SERVICE) as Vibrator
    vib.vibrate(milliseconds, repeat)
}

